package rpc.core.registry;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rpc.bootstrap.spring.RefererBean;
import rpc.bootstrap.spring.RegistryBean;
import rpc.bootstrap.spring.ServiceBean;
import rpc.core.InitClose;
import rpc.core.Registry;
import rpc.netty.tcp.NettyClient;

public class RegistryManager implements InitClose {

  private static final Logger logger = LoggerFactory.getLogger(RegistryManager.class);
  public volatile static Map<Class<?>, List<NettyClient>> nettyClientListMap = new HashMap<>();
  //  private List<Registry> registryList;
  private Registry registry;
  private ScheduledExecutorService executor;
  private List<ServiceBean> serviceBeanList;
  private List<RegistryBean> registryBeanList;
  private List<RefererBean> refererBeanList;

  private RegistryManager addRefererBean(RefererBean refererBean) {
    if (refererBeanList == null) {
      refererBeanList = new ArrayList<>();
    }
    refererBeanList.add(refererBean);
    return this;
  }

  public RegistryManager setRefererBeanList(List<RefererBean> refererBeanList) {
    this.refererBeanList = refererBeanList;
    return this;
  }

  public RegistryManager addRegistryBean(RegistryBean registryBean) {
    if (registryBeanList == null) {
      registryBeanList = new ArrayList<>();
    }
    this.registryBeanList.add(registryBean);
    return this;
  }

  public RegistryManager setRegistryBeanList(List<RegistryBean> registryBeanList) {
    this.registryBeanList = registryBeanList;
    return this;
  }

  public RegistryManager addServiceBean(ServiceBean serviceBean) {
    if (serviceBeanList == null) {
      serviceBeanList = new ArrayList<>();
    }
    serviceBeanList.add(serviceBean);
    return this;
  }

  public RegistryManager setServiceBeanList(List<ServiceBean> serviceBeanList) {
    this.serviceBeanList = serviceBeanList;
    return this;
  }

  public RegistryManager setRegistry(Registry registry) {
    this.registry = registry;
    return this;
  }

  @Override
  public void init() {
    if (Objects.isNull(registry)) {
      if (logger.isDebugEnabled()) {
        logger.debug("registryList is null,return");
      }
      return;
    }
    executor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("pool_registry_%d").build());
    executor.scheduleWithFixedDelay(this::registry, 1000 * 2, 1000 * 60, TimeUnit.MILLISECONDS);
    if (CollectionUtils.isNotEmpty(refererBeanList)) {
      executor.scheduleWithFixedDelay(this::discover, 0, 1000, TimeUnit.MILLISECONDS);
    }
  }

  private void discover() {

    refererBeanList.forEach(refererBean -> {
      List<NettyClient> nettyClients = nettyClientListMap.computeIfAbsent(refererBean.getInterfaceClass(), k -> new ArrayList<>())
          .stream().filter(NettyClient::isConnect).collect(Collectors.toList());
      registryBeanList.forEach(registryBean -> {
        List<RegistryItem> registryItems = registry.discover(refererBean, registryBean);
        List<RegistryItem> registryItemList = Optional.ofNullable(registryItems).orElse(new ArrayList<>());
        for (RegistryItem registryItem : registryItemList) {
          NettyClient nettyClient = new NettyClient(registryItem.getDirect(), registryItem.getId());
          if (nettyClients.contains(nettyClient)) {
            continue;
          }
          nettyClient.init();
          nettyClient.start();
          if (nettyClient.isConnect()) {
            nettyClients.add(nettyClient);
          }
        }
      });
      nettyClientListMap.put(refererBean.getInterfaceClass(), nettyClients);
    });
    if (logger.isDebugEnabled()) {
      logger.debug("nettyClientListMap:{}", nettyClientListMap.values().toArray(new Object[0]));
    }
  }

  private void registry() {
    registryBeanList.forEach(r -> serviceBeanList.forEach(s -> registry.register(s, r)));
  }

  @Override
  public void start() {
  }

  @Override
  public void close() {
    nettyClientListMap.values().stream().filter(Objects::nonNull).forEach(ncs -> ncs.stream().filter(Objects::nonNull).forEach(NettyClient::stop));
    registryBeanList.stream().filter(Objects::nonNull).forEach(r -> serviceBeanList.stream().filter(Objects::nonNull).forEach(s -> registry.deregister(s, r)));
    logger.info("deregister:rb:{},sb:{}", registryBeanList.size(), serviceBeanList.size());
  }
}